#include "monitorsstf.h"

//Constructor
monitorsstf::monitorsstf(logger *log) {
	this->log = log;
	pthread_mutex_init(&lock, NULL);
	pthread_mutex_init(&completeLock, NULL);
	pthread_cond_init(&inputCheck, NULL);
	pthread_cond_init(&schedWait, NULL);
	liveClients = 0;
	newReqFlag = false;
	reqMade = 0;
	reqProc = 0;
	lastTrack = 0;
}

//Destructor
monitorsstf::~monitorsstf() {
	buffer[0] = 0;
	pthread_mutex_destroy(&lock);
	pthread_mutex_destroy(&completeLock);
	pthread_cond_destroy(&inputCheck);
	pthread_cond_destroy(&schedWait);	
}

//Add request. Takes a single request and adds it to input DS. 
bool monitorsstf::addRequest(request* req) {
	pthread_mutex_lock(&lock);
	//Flag a new request has been made
	newReqFlag = true;
	inputQ.push(req);
	reqMade ++;	
	sprintf(buffer, "Request added. Client %d\nDisk track is %d, Disk work is %d", req->getRequestingTid(), req->getTrackNum(), req->getWorkSize());
	log->debug(buffer, "c");
	buffer[0] = 0;
	//Add the tracknum of the request to be logged.
	inputSequence.push(req->getTrackNum());
	//Signal the scheduler, who was waiting on empty input
	pthread_cond_signal(&schedWait);
	//Put this thread to sleep until signaled by worker
	pthread_cond_wait(&(req->CVWorkComplete), &lock);
	sprintf(buffer, "...Client %d finished waiting. Request finished.",req->getRequestingTid());
	log->debug(buffer, "c");
	buffer[0] = 0;
	pthread_mutex_unlock(&lock);
	return true;
}

//Schedules all requests in input DS by moving them to output DS. 
bool monitorsstf::scheduleRequests() {
	pthread_mutex_lock(&lock);
	sprintf(buffer, "Scheduler entering scheduleRequest()"); 
	log->debug(buffer, "c");
	buffer[0] = 0;
	queue<request*> sortQ;  
	bool ret_val = false;
	request* cur_req;
	request* min_req;
	int track_comp;
	//If theres no new input, wait on clients. 
	if (inputQ.empty() || !newReqFlag){
		sprintf(buffer, "Scheduler is waiting on clients, no new input.");
		log->debug(buffer, "c");
		buffer[0] = 0;
		pthread_cond_wait(&schedWait, &lock);
	}
	//Assert that there is indeed something to schedule
	if (newReqFlag && !inputQ.empty()) {
		// The sorting algorithm begins here
		int ref_track = lastTrack;               		
		// Place outputQ entries and inputQ entries into a single sorting queue
		while(!outputQ.empty()){
			cur_req = outputQ.front();
			outputQ.pop();
			sortQ.push(cur_req);      	
		}
		while(!inputQ.empty()){
			cur_req = inputQ.front();
			inputQ.pop();
			sortQ.push(cur_req); 
		}
		//Move the sortQ elements into the schedule, in proper order
		while(!sortQ.empty()){
			min_req = sortQ.front();
			sortQ.pop();
			track_comp = min_req->getTrackNum();
			while (track_comp == ref_track){                    		
				outputQ.push(min_req);
				sprintf(buffer, "Pushed request %d, on track %d  (1st push point)", min_req->getRequestingTid(), min_req->getTrackNum());
				log->debug(buffer, "c");
				if(!sortQ.empty()){
					min_req = sortQ.front();
					sortQ.pop();
					track_comp = min_req->getTrackNum();
				} else {
					min_req = NULL;
					track_comp = 16;
				}                 		
			}                    	
			for(int i = 0; i < sortQ.size(); i++){                        		
				cur_req = sortQ.front();
				sortQ.pop();
				if (abs(cur_req->getTrackNum() - ref_track) < abs(min_req->getTrackNum() - ref_track)){
					sortQ.push(min_req);
					min_req = cur_req;
				} else {
					sortQ.push(cur_req);
				}
			}
			if (track_comp != 16) {
				outputQ.push(min_req);  
				sprintf(buffer, "Pushed request %d, on track %d  (2nd push point)", min_req->getRequestingTid(), min_req->getTrackNum());
				log->debug(buffer, "c");  	                      	
				ref_track = min_req->getTrackNum(); 
			}     
                        	          
		}
		ret_val = true;
		newReqFlag = false;
		log->debug("Pending requests are as follows:", "c");
		for (int i = 0; i < outputQ.size(); i++) {
			cur_req = outputQ.front();
			outputQ.pop();
			sprintf(buffer, "%d. tid = %d, track no. = %d", i + 1, cur_req->getRequestingTid(), cur_req->getTrackNum());
			log->debug(buffer, "c"); 
			outputQ.push(cur_req);
		}
		//We've now moved elements into the outputQ, signal the worker. 
		pthread_cond_signal(&inputCheck);    
	}
	pthread_mutex_unlock(&lock);
	return ret_val;
}

//Process request. Removes a single request from the output DS. 
request* monitorsstf::removeRequest() {
	pthread_mutex_lock(&lock);
	
	sprintf(buffer, "Worker entering removeRequest().");
	log->debug(buffer, "c");
	buffer[0] = 0;
	
	//If there is nothing in the ouputQ, or a new request has been made, we have to wait
	if (newReqFlag || outputQ.empty()) {
		sprintf(buffer, "Worker is waiting on scheduler; new requests made, or outputQ empty.");
		log->debug(buffer, "a");
		buffer[0] = 0;
		pthread_cond_wait(&inputCheck, &lock);
	}
	//Retrieve front element
	request* temp;
	if (!outputQ.empty()) {
		temp = outputQ.front();
		outputQ.pop();
		reqProc ++;
		lastTrack = temp->getTrackNum();
		outputSequence.push(lastTrack);
		sprintf(buffer, "Request processed");
		log->debug(buffer, "b");
		buffer[0] = 0;
	} else { 
		temp = NULL;
	}
	pthread_mutex_unlock(&lock);
	return(temp);
}

/*
 *Once worker has processed the request and modified the request object
 *(code not needed to be protected), pass back the request object to the monitor
 * so we can signal the reqest objects condition_variable, and wake up the client
 */
void monitorsstf::signalClient(request* req) {
	pthread_mutex_lock(&lock);
	pthread_cond_signal(&(req->CVWorkComplete));
	pthread_mutex_unlock(&lock);
}

//Empty the inputSequence, printing each element to log
void monitorsstf::printInputSequence(){
	while(!inputSequence.empty()) {
		sprintf(buffer, "%d", inputSequence.front());
		log->debug(buffer, "m");
		buffer[0] = 0;
		inputSequence.pop();
	}
}

//Empty the outputSequence, printing each element to log
void monitorsstf::printOutputSequence(){
	while(!outputSequence.empty()) {
		sprintf(buffer, "%d", outputSequence.front());
		log->debug(buffer, "m");
		buffer[0] = 0;
		outputSequence.pop();
	}
} 

/////////////////////////////////////////
// Methods for checking end conditions. 
/////////////////////////////////////////

bool monitorsstf::isWorkLeft() {
	pthread_mutex_lock(&lock);
	bool retVal = true;
	if (inputQ.empty() && outputQ.empty()) {
		retVal = false;
	}
	pthread_mutex_unlock(&lock);
	return retVal;
}

int monitorsstf::getLiveClients() {
	pthread_mutex_lock(&completeLock);
	int retVal = liveClients;
	pthread_mutex_unlock(&completeLock);
	return retVal;
}

void monitorsstf::decLiveClients() {
	pthread_mutex_lock(&completeLock);
	liveClients--;
	pthread_mutex_unlock(&completeLock);
}

void monitorsstf::incLiveClients() {
	pthread_mutex_lock(&completeLock);
	liveClients++;	
	pthread_mutex_unlock(&completeLock);
}

int monitorsstf::getReqDiff() {
	return (reqMade - reqProc);
}

int monitorsstf::getReqProc() {
	return reqProc;
}
